package com.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.servlet.ServletContext;

import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperPrintManager;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.util.JRLoader;

import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.hibernate.internal.SessionFactoryImpl;
import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;
import org.primefaces.model.UploadedFile;
import org.springframework.web.jsf.FacesContextUtils;

import com.builder.ClienteBuilder;
import com.builder.FacturacionBuilder;
import com.builder.ProductoBuilder;
import com.dao.BaseModelDAO;
import com.model.Cliente;
import com.model.Facturacion;
import com.model.Parametro;
import com.model.ProductoPrecio;
import com.model.Tarifario;
import com.query.FacturacionQuery;
import com.query.TarifarioQuery;
import com.type.TipoFacturaEnum;
import com.type.TipoTarifarioEnum;
import com.util.FilesystemUtil;
import com.util.JSFUtil;
import com.util.ValidatorUtil;
import com.view.ClienteView;
import com.view.FacturacionDetalleView;
import com.view.FacturacionView;

@ManagedBean
@ViewScoped
@SuppressWarnings("serial")
public class FacturacionController extends PaginableController<Facturacion> {

	private static final String facturacionTolerancia = "facturacionTolerancia";
	private static final String facturacionCantidadProductos = "facturacionCatidadProductos";
	private static final String facturacionPorcentajeVariacion = "facturacionPorcentajeVariacion";

	private Logger log = Logger.getLogger(FacturacionController.class);
	private BaseModelDAO<Facturacion> dao;
	private BaseModelDAO<Cliente> clienteDao;
	private BaseModelDAO<Tarifario> tarifarioDao;
	private BaseModelDAO<Parametro> parametroDao;
	private Facturacion model;
	private FacturacionQuery query;
	private List<ClienteView> listaCliente;
	private UploadedFile archivo;
	private Boolean imprimirFacturas;
	private String mensajeFacturas;
	private List<Facturacion> listaFacturacionModel;

	@ManagedProperty("#{facturacionView}")
	private FacturacionView view;

	@ManagedProperty("#{facturacionBuilder}")
	private FacturacionBuilder builder;

	@ManagedProperty("#{clienteBuilder}")
	private ClienteBuilder clienteBuilder;

	@ManagedProperty("#{productoBuilder}")
	private ProductoBuilder productoBuilder;

	@SuppressWarnings("unchecked")
	public FacturacionController() {

		try {
			dao = (BaseModelDAO<Facturacion>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("facturacionDAO");
			clienteDao = (BaseModelDAO<Cliente>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("clienteDAO");
			tarifarioDao = (BaseModelDAO<Tarifario>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("tarifarioDAO");
			parametroDao = (BaseModelDAO<Parametro>) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("parametroDAO");

			query = new FacturacionQuery();

			imprimirFacturas = Boolean.TRUE;

			addEdit = false;

		} catch (Throwable e) {
			log.error("Error al inicializar la clase FacturacionController", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@PostConstruct
	public void init() {
		listaCliente = clienteBuilder.toView(clienteDao.getList());
	}

	public FacturacionBuilder getBuilder() {
		return builder;
	}

	public void setBuilder(FacturacionBuilder builder) {
		this.builder = builder;
	}

	public ClienteBuilder getClienteBuilder() {
		return clienteBuilder;
	}

	public void setClienteBuilder(ClienteBuilder clienteBuilder) {
		this.clienteBuilder = clienteBuilder;
	}

	public Facturacion getModel() {
		return model;
	}

	public FacturacionQuery getQuery() {
		return query;
	}

	public List<ClienteView> getListaCliente() {
		return listaCliente;
	}

	public FacturacionView getView() {
		return view;
	}

	public void setView(FacturacionView view) {
		this.view = view;
	}

	public UploadedFile getArchivo() {
		return archivo;
	}

	public void setArchivo(UploadedFile archivo) {
		this.archivo = archivo;
	}

	public Boolean getImprimirFacturas() {
		return imprimirFacturas;
	}

	public void setImprimirFacturas(Boolean imprimirFacturas) {
		this.imprimirFacturas = imprimirFacturas;
	}

	public ProductoBuilder getProductoBuilder() {
		return productoBuilder;
	}

	public void setProductoBuilder(ProductoBuilder productoBuilder) {
		this.productoBuilder = productoBuilder;
	}

	@Override
	public void query(ActionEvent event) {
		loadList();
	}

	@Override
	public void edit(ActionEvent event) {

		// No se puede editar.

	}

	public String getMensajeFacturas() {
		return mensajeFacturas;
	}

	public void setMensajeFacturas(String mensajeFacturas) {
		this.mensajeFacturas = mensajeFacturas;
	}

	public List<Facturacion> getListaFacturacionModel() {
		return listaFacturacionModel;
	}

	@Override
	public void delete(ActionEvent event) {

		try {
			model = (Facturacion) lazyDM.getRowData();
			dao.delete(model);
			loadList();
		} catch (Throwable e) {
			log.error("Error al eliminar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	@Override
	public void add(ActionEvent event) {
		clear();
		addEdit = true;
	}

	@Override
	public void save(ActionEvent event) {

		try {

			List<String> errores = new ValidatorUtil().validate(view);

			if (errores.size() == 0) {

				model = builder.toDomain(view);

				if (model.getID() != null) {
					dao.edit(model);
					addEdit = false;
				} else {
					dao.save(model);
				}
				clear();
				JSFUtil.saveMessage("Elemento guardado con exito",
						FacesMessage.SEVERITY_INFO);

				if (!addEdit) {
					loadList();
				}
			} else {
				JSFUtil.saveMessages(errores);
			}

		} catch (Throwable e) {
			log.error("Error al guardar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	public void saveFromFile(ActionEvent event) {

		try {

			if (archivo != null) {

				// parseo el archivo subido (No se guarda el archivo)
				procesarArchivo();

				clear();

			} else {
				JSFUtil.saveMessage("Debe seleccionar un archivo",
						FacesMessage.SEVERITY_ERROR);
			}

		} catch (Throwable e) {
			log.error("Error al guardar", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	public void imprimirFacturas(ActionEvent event) {

		try {

			for (Facturacion facturacion : listaFacturacionModel) {
				imprimirFactura(facturacion.getID());
			}

		} catch (Throwable e) {
			log.error("Error al imprimir las facturas", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}
	}

	private void procesarArchivo() {

		FileInputStream file = null;
		try {

			file = new FileInputStream(FilesystemUtil.streamAFile(archivo
					.getInputstream()));

			// Abro el archivo
			HSSFWorkbook workbook = new HSSFWorkbook(file);

			// Tomo la primer solapa de factura A
			HSSFSheet sheetFacturasA = workbook.getSheetAt(0);

			// Tomo la primer solapa de factura B
			HSSFSheet sheetFacturasB = workbook.getSheetAt(1);

			// Tomo la primer solapa de factura C
			HSSFSheet sheetFacturasC = workbook.getSheetAt(2);

			// Tomo la primer solapa de factura M
			HSSFSheet sheetFacturasM = workbook.getSheetAt(3);

			try {
				List<FacturacionView> listFacturacionCompleta = new ArrayList<FacturacionView>();

				// transformo las filas a objetos y los almaceno en la base
				List<FacturacionView> listaFacturasA = obtenerDatosFilas(
						sheetFacturasA, TipoFacturaEnum.A,
						archivo.getFileName());
				listFacturacionCompleta.addAll(listaFacturasA);

				// transformo las filas a objetos y los almaceno en la base
				List<FacturacionView> listaFacturasB = obtenerDatosFilas(
						sheetFacturasB, TipoFacturaEnum.B,
						archivo.getFileName());
				listFacturacionCompleta.addAll(listaFacturasB);

				// transformo las filas a objetos y los almaceno en la base
				List<FacturacionView> listaFacturasC = obtenerDatosFilas(
						sheetFacturasC, TipoFacturaEnum.C,
						archivo.getFileName());
				listFacturacionCompleta.addAll(listaFacturasC);

				// transformo las filas a objetos y los almaceno en la base
				List<FacturacionView> listaFacturasM = obtenerDatosFilas(
						sheetFacturasM, TipoFacturaEnum.M,
						archivo.getFileName());
				listFacturacionCompleta.addAll(listaFacturasM);

				// Le agrego el detalle de los productos a la facturas. Los
				// montos tienen que coincidir con lo de la planilla.

				// hay que buscar los tarifarios vigentes de tipo clientes y en
				// función de lo que el tarifario tiene ir tomano productos
				// hasta completar la boleta del cliente
				// jugar conlas cnatidades de los productos. en caso de que no
				// haya un tarifario vigente se puede tomar uno anterior.
				generarDetalleDeFacturacion(listFacturacionCompleta);

				// guardo todas las facturas juntas por un tema de integridad de
				// datos
				listaFacturacionModel = builder
						.toDomain(listFacturacionCompleta);

				dao.saveList(listaFacturacionModel);

				int facturasA = 0;
				int facturasB = 0;
				int facturasC = 0;
				int facturasM = 0;

				// Cuanta la cantidad de facturas a imprimir para mostrarlas por
				// pantalla
				for (Facturacion facturacion : listaFacturacionModel) {
					// imprimirFactura(facturacion.getID());
					if (facturacion.getTipoFactura().equals(TipoFacturaEnum.A)) {
						facturasA++;
					} else if (facturacion.getTipoFactura().equals(
							TipoFacturaEnum.B)) {
						facturasB++;
					} else if (facturacion.getTipoFactura().equals(
							TipoFacturaEnum.C)) {
						facturasC++;
					} else if (facturacion.getTipoFactura().equals(
							TipoFacturaEnum.M)) {
						facturasM++;
					}
				}

				mensajeFacturas = "Se procesaron " + facturasA
						+ " factura/s A, " + facturasB + " factura/s B, "
						+ facturasC + " factura/s C y " + facturasM
						+ " factura/s M.";

				if (imprimirFacturas) {
					mensajeFacturas = mensajeFacturas
							+ "\n\n Por favor prepare las facturas en la impresora en el orden antes mencionado antes de continuar.";
				}

				JSFUtil.saveMessage("Se procesó correctamente el archivo "
						+ archivo.getFileName(), FacesMessage.SEVERITY_INFO);
			} catch (Exception e) {
				JSFUtil.saveMessage(e.getMessage(), FacesMessage.SEVERITY_ERROR);
			}

		} catch (Exception e) {
			log.error("Error al parsear el archivo " + archivo.getFileName(), e);
		} finally {
			try {
				file.close();
			} catch (IOException e) {
			}
		}

	}

	/**
	 * 
	 * Genera el detalle de cada factura en función del monto y el tarifario
	 * vigente
	 * @throws Exception 
	 * 
	 */
	private void generarDetalleDeFacturacion(
			List<FacturacionView> listaFacturacion) throws Exception {

		// tarifario actual
		TarifarioQuery tq = new TarifarioQuery(null, TipoTarifarioEnum.CLIENTE,
				null, null, null, false, new Date());
		List<Tarifario> listaTarifario = tarifarioDao.getList(tq);

		if (listaTarifario.size() > 0) {
			Tarifario tarifario = listaTarifario.get(0);

			tarifario = tarifarioDao.findFULL(tarifario.getID());

			// Obtengo el valor de la configuracion, si no está la configuración
			// pongo una por defecto
			BigDecimal tolerancia;
			try {
				tolerancia = new BigDecimal(parametroDao.get(
						facturacionTolerancia).getValor());
			} catch (Throwable t) {
				tolerancia = new BigDecimal(0.1);
			}

			int contador = 0;

			// recorro la lista de facturas a generar el detalle
			for (FacturacionView fv : listaFacturacion) {
				contador++;

				if (contador == 18) {
					System.out.println("registro 18");
				}
				generarDetalleFactura(tarifario, fv, tolerancia);
				System.out.println("facturas procesadas : " + contador);
			}

		} else {
			// no hay tarifarios vigentes
			throw new Exception("Error: No hay tarifarios para clientes vigentes.");
		}

	}

	/**
	 * Tiene que generar la tarifa justa teniendo en cuenta el monto que tiene
	 * que dar
	 * 
	 * @param tarifario
	 * @param facturacion
	 */
	private void generarDetalleFactura(Tarifario tarifario,
			FacturacionView facturacion, BigDecimal tolerancia) {

		if (tarifario.getListaProductoPrecio().size() > 0) {

			BigDecimal monto = BigDecimal.ZERO;

			// redondeo el total a dos decimales
			facturacion.setTotal(facturacion.getTotal().setScale(2,
					RoundingMode.HALF_UP));

			// Cantidad máxima de productos
			// se toma del parámetro o se pone uno por defecto
			int rangoCantidad = 0;
			try {
				rangoCantidad = new Integer(parametroDao.get(
						facturacionCantidadProductos).getValor());
			} catch (Throwable t) {
				rangoCantidad = 10;
			}

			// porcentaje de variación aceptado
			// se toma del parámetro o se pone uno por defecto
			int porcentajeVariacionParametro;
			try {
				porcentajeVariacionParametro = new Integer(parametroDao.get(
						facturacionPorcentajeVariacion).getValor());
			} catch (Throwable t) {
				porcentajeVariacionParametro = 25;
			}

			// Agrego producto y los ajusto hasta que este dentro de la
			// tolerancia
			while (diferenciaPorcentual(facturacion.getTotal(), monto).abs()
					.compareTo(tolerancia) > 0) {

				System.out.println("Difrerencia porcentual : "
						+ diferenciaPorcentual(facturacion.getTotal(), monto));

				Random random = new Random();
				int indexList = random.nextInt(tarifario
						.getListaProductoPrecio().size());

				int cantidad = random.nextInt(rangoCantidad);

				// porcentaje de variación permitido
				BigDecimal porcentajeVariacion = new BigDecimal(
						porcentajeVariacionParametro);

				if (cantidad == 0) {
					cantidad = 1;
				}

				if (indexList == tarifario.getListaProductoPrecio().size()) {
					indexList = indexList - 1;
				}

				// hay que agregar detalles hasta que no de y
				// tengamos que modificar las tarifas por un porcentaje X%

				// tomo un producto al azar
				ProductoPrecio pp = tarifario.getListaProductoPrecio().get(
						indexList);

				// veo si puedo agregar el producto con la cantidad que el monto
				// sea menor a el total de la facturación.
				// puede ser menor al 25 % o igual e inflo los precios
				if (monto.compareTo(facturacion.getTotal()) < 0) {

					// veo si puedo ajustar el total por si está dentro de la
					// tolerancia
					if (diferenciaPorcentual(facturacion.getTotal(), monto)
							.abs().compareTo(porcentajeVariacion) <= 0) {

						System.out
								.println("Ajusto el monto porque hay una diferencia de "
										+ diferenciaPorcentual(
												facturacion.getTotal(), monto)
										+ " % . Monto inicial "
										+ monto
										+ " .Total " + facturacion.getTotal());

						monto = ajustarDetalles(
								facturacion,
								diferenciaPorcentual(facturacion.getTotal(),
										monto), monto);
						System.out.println("Monto Ajustado :" + monto);

					} else {

						// veo si hay que seguir agregando
						BigDecimal montoAuxiliar = BigDecimal.ZERO;

						montoAuxiliar = montoAuxiliar.add(monto).add(
								pp.getPrecio().multiply(
										new BigDecimal(cantidad)));

						// veo que si agregando el producto por la cantidad me
						// da el
						// precio o me paso
						if (facturacion.getTotal().compareTo(montoAuxiliar) >= 0) {
							monto = monto.add(pp.getPrecio().multiply(
									new BigDecimal(cantidad)));
							// Agrego el producto
							FacturacionDetalleView fdv = new FacturacionDetalleView(
									null, pp.getPrecio(), pp.getPrecio(),
									cantidad, productoBuilder.toView(pp
											.getProducto()));
							facturacion.getListaFacturacionDetalle().add(fdv);

							System.out
									.println(" Agrego Un producto por "
											+ pp.getPrecio().multiply(
													new BigDecimal(cantidad))
											+ " porque todav{ia me faltaba para llegar a "
											+ facturacion.getTotal()
											+ ". Ahora voy " + monto);

							// como me pase del monto que quiero y veo si el
							// porcentaje de variación me da. Si es así agrego
							// el
							// producto
						} else if (facturacion.getTotal().compareTo(
								montoAuxiliar) < 0
								&& diferenciaPorcentual(facturacion.getTotal(),
										monto).abs().compareTo(
										porcentajeVariacion) <= 0) {
							monto = monto.add(pp.getPrecio().multiply(
									new BigDecimal(cantidad)));
							// Agrego el producto
							FacturacionDetalleView fdv = new FacturacionDetalleView(
									null, pp.getPrecio(), pp.getPrecio(),
									cantidad, productoBuilder.toView(pp
											.getProducto()));
							facturacion.getListaFacturacionDetalle().add(fdv);
							System.out
									.println("Agregue un producto y me pase de "
											+ facturacion.getTotal()
											+ " con un valor de " + monto);
						}

					}

				} else if (monto.compareTo(facturacion.getTotal()) == 0) {
					break;
				} else if (monto.compareTo(facturacion.getTotal()) > 0) {
					// me pasé hay que ajustar los valores
					monto = ajustarDetalles(
							facturacion,
							diferenciaPorcentual(facturacion.getTotal(), monto),
							monto);
				}

			}

		}
	}

	/**
	 * Ajusta el precio de los productos para que de el monto de la factura
	 * buscada
	 * 
	 * @param facturacion
	 * @param diferenciaPorcentual
	 * 
	 * @return monto total ajutado
	 */
	private BigDecimal ajustarDetalles(FacturacionView facturacion,
			BigDecimal diferenciaPorcentual, BigDecimal totalParcial) {

		BigDecimal monto = BigDecimal.ZERO;

		// Saco cuanto es el la diferencia en monto sobre el total
		BigDecimal montoDiferencia = diferenciaPorcentual.multiply(
				facturacion.getTotal()).divide(new BigDecimal(100),
				RoundingMode.HALF_UP);

		// Calculo cuanto es este monto en el total parcial
		BigDecimal diferenciaPorcentualParcial = montoDiferencia.multiply(
				new BigDecimal(100)).divide(totalParcial, RoundingMode.HALF_UP);

		for (FacturacionDetalleView fdv : facturacion
				.getListaFacturacionDetalle()) {

			if (diferenciaPorcentual.compareTo(BigDecimal.ZERO) > 0) {

				// estoy subiendo el X porciento del parcial pero tengo que
				// subir el x porciento del total que no es lo mismo.
				// inflo los precios el porcentaje configurado para que de el
				// valor
				fdv.setPrecioAdaptado(fdv.getPrecioOriginal().add(
						fdv.getPrecioOriginal()
								.multiply(diferenciaPorcentualParcial)
								.divide(new BigDecimal(100),
										RoundingMode.HALF_UP)));
			} else if (diferenciaPorcentual.compareTo(BigDecimal.ZERO) < 0) {
				// estoy subiendo el X porciento del parcial pero tengo que
				// subir el x porciento del total que no es lo mismo.
				// desinflo los precios para que de el valor esperado
				fdv.setPrecioAdaptado(fdv.getPrecioOriginal().subtract(
						(fdv.getPrecioOriginal().multiply(
								diferenciaPorcentualParcial).divide(
								new BigDecimal(100), RoundingMode.HALF_UP))));
			}

			monto = monto.add(fdv.getPrecioAdaptado().multiply(
					new BigDecimal(fdv.getCantidad())));
			monto = monto.setScale(2, BigDecimal.ROUND_HALF_UP);
		}

		// ver que paso con esto maldito fddd
		monto = monto.setScale(2, BigDecimal.ROUND_HALF_UP);
		return monto;
	}

	/**
	 * 
	 * REtorna el porcentaje de diferencia
	 * 
	 * 
	 * @param total
	 * @param totalParcial
	 * @return retorna la diferencia porcentual en positivo cuando el parciales
	 *         mas grande o negativo cuando el parcial es más chico
	 * 
	 */
	private BigDecimal diferenciaPorcentual(BigDecimal total,
			BigDecimal totalParcial) {
		BigDecimal diferencia = BigDecimal.ZERO;
		diferencia = diferencia.setScale(2, BigDecimal.ROUND_HALF_UP);
		total = total.setScale(2, BigDecimal.ROUND_HALF_UP);

		if (totalParcial.compareTo(BigDecimal.ZERO) == 0) {
			diferencia = new BigDecimal(100);
		} else {

			diferencia = totalParcial.multiply(new BigDecimal(100));
			diferencia = diferencia.divide(total, RoundingMode.HALF_UP);

			if (diferencia.compareTo(new BigDecimal(100)) < 0
					|| diferencia.compareTo(new BigDecimal(100)) > 0) {
				diferencia = new BigDecimal(100).subtract(diferencia);
			} else {
				diferencia = BigDecimal.ZERO;
			}

		}

		return diferencia;
	}

	private List<FacturacionView> obtenerDatosFilas(HSSFSheet sheet,
			TipoFacturaEnum tipoFactura, String nombreArchivo) throws Exception {

		List<FacturacionView> listaFacturacion = new ArrayList<FacturacionView>();

		List<String> errores = new ArrayList<String>();

		// Recorro las filas
		Iterator<Row> rowIterator = sheet.iterator();
		while (rowIterator.hasNext()) {
			Row row = rowIterator.next();

			FacturacionView view = new FacturacionView();

			boolean tieneErrores = false;
			String cuit = "";
			String dni = "";

			try {

				// busco desde la tercer fila
				if (row.getRowNum() > 1) {

					// a la primer fila vacía no sigo recoriendo el Excel
					if (isRowEmpty(row)) {
						break;
					}

					view.setNombreArchivo(nombreArchivo);

					// celda 1 - Número de factura
					Cell cell1 = row.getCell((short) 0);

					try {
						cuit = cell1.getStringCellValue();
					} catch (Throwable t) {
						try {
							Double numero = cell1.getNumericCellValue();
							cuit = Integer.toString(numero.intValue());
						} catch (Throwable t1) {
							tieneErrores = true;
							errores.add("Error de foramto en fila "
									+ (row.getRowNum() + 1) + ". Columna "
									+ (cell1.getColumnIndex() + 1)
									+ ". Debe tener formato texto. Solapa "
									+ sheet.getSheetName());
						}
					}

					// celda 2 - fecha -
					Cell cell2 = row.getCell((short) 1);

					if (cell2.getCellType() == Cell.CELL_TYPE_NUMERIC) {
						try {
							view.setFecha(cell2.getDateCellValue());
						} catch (Exception e) {
							tieneErrores = true;
							errores.add("Error de foramto en fila "
									+ (row.getRowNum() + 1) + ". Columna "
									+ cell2.getColumnIndex() + 1
									+ ". Debe tener formato fecha. Solapa "
									+ sheet.getSheetName());
						}
					}

					// celda 3 - neto o total dependiendo del tipo de fatura
					Cell cell3 = row.getCell((short) 2);

					if (cell3.getCellType() == Cell.CELL_TYPE_NUMERIC
							|| cell3.getCellType() == Cell.CELL_TYPE_FORMULA) {
						if (tipoFactura == TipoFacturaEnum.A
								|| tipoFactura == TipoFacturaEnum.M) {
							view.setNeto(new BigDecimal(cell3
									.getNumericCellValue()));
						} else if (tipoFactura == TipoFacturaEnum.B
								|| tipoFactura == TipoFacturaEnum.C) {
							view.setTotal(new BigDecimal(cell3
									.getNumericCellValue()));
						}
					} else {
						tieneErrores = true;
						errores.add("Error de foramto en fila "
								+ (row.getRowNum() + 1) + ". Columna "
								+ (cell3.getColumnIndex() + 1)
								+ ". Debe tener formato numérico. Solapa "
								+ sheet.getSheetName());
					}

					// celda 4 - IVA o cliente dependiedo del tipo de factura
					Cell cell4 = row.getCell((short) 3);
					// IVA
					if (tipoFactura == TipoFacturaEnum.A
							|| tipoFactura == TipoFacturaEnum.M) {
						if (cell4.getCellType() == Cell.CELL_TYPE_NUMERIC
								|| cell4.getCellType() == Cell.CELL_TYPE_FORMULA) {
							view.setIva(new BigDecimal(cell4
									.getNumericCellValue()));
						} else {
							tieneErrores = true;
							errores.add("Error de foramto en fila "
									+ (row.getRowNum() + 1) + ". Columna "
									+ (cell4.getColumnIndex() + 1)
									+ ". Debe tener formato numérico. Solapa "
									+ sheet.getSheetName());
						}
						// CUIT
					} else if (tipoFactura == TipoFacturaEnum.B
							|| tipoFactura == TipoFacturaEnum.C) {
						try {
							cuit = cell4.getStringCellValue();
						} catch (Throwable t) {
							try {
								Double numero = cell4.getNumericCellValue();
								cuit = Integer.toString(numero.intValue());
							} catch (Throwable t1) {
								tieneErrores = true;
								errores.add("Error de foramto en fila "
										+ (row.getRowNum() + 1) + ". Columna "
										+ (cell4.getColumnIndex() + 1)
										+ ". Debe tener formato texto. Solapa "
										+ sheet.getSheetName());
							}
						}
					}

					// celda 5 - total o DNI dependiendo del tipo de factura
					Cell cell5 = row.getCell((short) 4);

					if (tipoFactura == TipoFacturaEnum.A
							|| tipoFactura == TipoFacturaEnum.M) {
						if (cell5.getCellType() == Cell.CELL_TYPE_NUMERIC
								|| cell5.getCellType() == Cell.CELL_TYPE_FORMULA) {
							view.setTotal(new BigDecimal(cell5
									.getNumericCellValue()));
						} else {
							tieneErrores = true;
							errores.add("Error de foramto en fila "
									+ (row.getRowNum() + 1) + ". Columna "
									+ (cell5.getColumnIndex() + 1)
									+ ". Debe tener formato numérico. Solapa "
									+ sheet.getSheetName());
						}
					} else if (tipoFactura == TipoFacturaEnum.B
							|| tipoFactura == TipoFacturaEnum.C) {
						try {
							dni = cell5.getStringCellValue();
						} catch (Throwable t) {

							try {
								Double numero = cell5.getNumericCellValue();
								dni = Integer.toString(numero.intValue());
							} catch (Throwable t1) {
								tieneErrores = true;
								errores.add("Error de foramto en fila "
										+ (row.getRowNum() + 1) + ". Columna "
										+ (cell5.getColumnIndex() + 1)
										+ ". Debe tener formato texto. Solapa "
										+ sheet.getSheetName());
							}
						}

					}

					// solo para facturas A o M
					if (tipoFactura == TipoFacturaEnum.A
							|| tipoFactura == TipoFacturaEnum.M) {
						// celda 6 - CUIT
						Cell cell6 = row.getCell((short) 5);

						try {
							cuit = cell6.getStringCellValue();
						} catch (Throwable t) {
							try {
								Double numero = cell6.getNumericCellValue();
								cuit = Integer.toString(numero.intValue());
							} catch (Throwable t1) {
								tieneErrores = true;
								errores.add("Error de foramto en fila "
										+ (row.getRowNum() + 1) + ". Columna "
										+ (cell6.getColumnIndex() + 1)
										+ ". Debe tener formato texto. Solapa "
										+ sheet.getSheetName());
							}
						}

						// celda 7 - DNI
						Cell cell7 = row.getCell((short) 6);

						try {
							dni = cell7.getStringCellValue();
						} catch (Throwable t) {

							try {
								Double numero = cell7.getNumericCellValue();
								dni = Integer.toString(numero.intValue());
							} catch (Throwable t1) {
								tieneErrores = true;
								errores.add("Error de foramto en fila "
										+ (row.getRowNum() + 1) + ". Columna "
										+ (cell7.getColumnIndex() + 1)
										+ ". Debe tener formato texto. Solapa "
										+ sheet.getSheetName());
							}
						}
					}

					Cliente cliente = null;
					if (!"".equals(cuit)) {
						List<Cliente> listaCliente = clienteDao.getList(cuit);
						if (listaCliente.size() > 0) {
							cliente = listaCliente.get(0);
						}
					} else if (!"".equals(dni) && cliente == null) {
						List<Cliente> listaCliente = clienteDao.getList(dni);
						if (listaCliente.size() > 0) {
							cliente = listaCliente.get(0);
						}
					}

					if (cliente != null) {
						view.setCliente(clienteBuilder.toView(cliente));
					} else {
						tieneErrores = true;
						errores.add("El CUIT o DNI del cliente no se corresponde con ningún cliente del sistema. Por favor revise la fila número :"
								+ (row.getRowNum() + 1)
								+ " en las columnas CUIT o DNI. Solapa "
								+ sheet.getSheetName());
					}

					// coloco el tipo de factura
					view.setTipoFactura(tipoFactura);

					// si no hubo errores lo agrego a la lista de facturas a
					// guardar
					if (!tieneErrores) {
						listaFacturacion.add(view);
					}
				}
			} catch (Throwable t) {
				errores.add(t.getMessage());
			}

		}

		if (errores.size() > 0) {
			throw new Exception(errores.toString());
		}

		return listaFacturacion;
	}

	/**
	 * Se fija una fila está vacía. Sirve para no parsear de más
	 */
	private boolean isRowEmpty(Row row) {
		for (int c = row.getFirstCellNum(); c <= row.getLastCellNum(); c++) {
			Cell cell = row.getCell(c);
			if (cell != null && cell.getCellType() != Cell.CELL_TYPE_BLANK)
				return false;
		}
		return true;
	}

	@Override
	public void cancel(ActionEvent event) {
		addEdit = false;
		lazyDM = null;
		clear();
	}

	@Override
	public void clear() {
		view = new FacturacionView();
		model = new Facturacion();
		archivo = null;
	}

	private void loadList() {

		lazyDM = new LazyDataModel<Facturacion>() {
			@Override
			public List<Facturacion> load(int first, int pageSize,
					String sortField, SortOrder sortOrder,
					Map<String, Object> filters) {

				Map<String, Object> filtro = new HashMap<String, Object>();
				filtro.put("fecha", query.getFecha());
				filtro.put("cliente.id", query.getClienteID());

				return dao.getList(first, pageSize, "fecha", false, filtro,
						true);
			}
		};

		Map<String, Object> filtro = new HashMap<String, Object>();
		filtro.put("fecha", query.getFecha());
		filtro.put("cliente.id", query.getClienteID());
		lazyDM.setRowCount(dao.count(filtro, true).intValue());
	}

	public void handleFileUpload(FileUploadEvent event) {

		JSFUtil.saveMessage(event.getFile().getFileName()
				+ " cargado correctamente.", FacesMessage.SEVERITY_INFO);
		archivo = event.getFile();
	}

	@SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
	private void imprimirFactura(Long facturaID) {
		try {

			ServletContext sc = (ServletContext) FacesContext
					.getCurrentInstance().getExternalContext().getContext();
			String pathReal = sc.getRealPath("/");
			File file = new File(pathReal + File.separator
					+ "resources\\reports\\facturacion.jasper");

			JasperReport jasperReport = (JasperReport) JRLoader
					.loadObject(file);

			// parametros
			Map parameters = new HashMap();
			parameters.put("idFacturacion", facturaID);

			SessionFactoryImpl objetc = (SessionFactoryImpl) FacesContextUtils
					.getWebApplicationContext(FacesContext.getCurrentInstance())
					.getBean("sessionFactory");

			JasperPrint print = JasperFillManager.fillReport(jasperReport,
					parameters, objetc.getJdbcServices()
							.getConnectionProvider().getConnection());

			// imprime en la impresora directamente
			JasperPrintManager.printReport(print, false);

		} catch (Throwable e) {
			log.error("Error al imprimir la factura con id " + facturaID
					+ " a PDF: ", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al imprimer la factura con identificador "
									+ facturaID, ""));
		}
	}

	public void print(ActionEvent event) {

		try {
			model = lazyDM.getRowData();
			imprimirFactura(model.getID());
			JSFUtil.saveMessage(
					"Se envio el elemento a la impresora de forma exitosa",
					FacesMessage.SEVERITY_INFO);
			// recargo la página
			JSFUtil.reloadPage();
		} catch (Throwable e) {
			log.error("Error al imprimir", e);
			FacesContext.getCurrentInstance().addMessage(
					null,
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Error al realizar la operacion", ""));
		}

	}

}
